/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-08-09     admin       the first version
 */
#include "rtthread.h"
#include "board.h"
#include <rtdevice.h>

struct gd32_pwm
{
    struct rt_device_pwm pwm_device;
    uint32_t tim_handle;
    uint32_t gpio_periph;
    uint8_t gpio_pin;
    uint8_t channel;
    char *name;
};

struct gd32_pwm gd32_pwm_list[]=
{

#ifdef BSP_USING_PWM1
    {
        .tim_handle=TIMER1,
        .channel=TIMER_CH_1,
        .gpio_periph=GPIOA,
        .gpio_pin=GPIO_PIN_1,
        .name="pwm1"
    },
#endif
};

static int gd32_pwm_init(void);
static rt_err_t  gd32_hw_pwm_init(struct gd32_pwm *device);
static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
static rt_err_t gd32_pwm_enable(uint32_t time,struct rt_pwm_configuration *configuration, rt_bool_t enable);

static struct rt_pwm_ops drv_ops =
{
    drv_pwm_control
};

static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
{
    if(device==RT_NULL||arg==RT_NULL)
    {
        return RT_ERROR;
    }
    struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
    uint32_t time=*((uint32_t*)(device->parent.user_data));
    switch (cmd)
    {
        case PWM_CMD_ENABLE:
            return gd32_pwm_enable(time,configuration,RT_TRUE);
        case PWM_CMD_DISABLE:
            return gd32_pwm_enable(time,configuration,RT_FALSE);
        case PWM_CMD_SET:
            //return gd32_pwm_set(time, configuration);
        default:
            return RT_EINVAL;
    }
    return RT_EOK;
}
static rt_err_t gd32_pwm_enable(uint32_t time,struct rt_pwm_configuration *configuration, rt_bool_t enable)
{

    if(enable==RT_TRUE)
    {
        rt_kprintf("enable pwm channel %d\n",configuration->channel);
        timer_enable(time);
        timer_channel_output_state_config(time,configuration->channel,TIMER_CCX_ENABLE);
    }else
    {
        rt_kprintf("disable pwm channel %d\n",configuration->channel);
        timer_channel_output_state_config(time,configuration->channel,TIMER_CCX_DISABLE);
        //timer_disable(time);
    }
    return RT_EOK;
}
static rt_err_t  gd32_hw_pwm_init(struct gd32_pwm *device)
{
    if(device==RT_NULL)
    {
        return -RT_ERROR;
    }
    rcu_periph_enum rcu_per=0;
    switch(device->tim_handle)
    {
        case TIMER1:
            rcu_per=RCU_TIMER1;
            if(device->channel==TIMER_CH_1)
            {
                rcu_periph_clock_enable(RCU_GPIOA);
                rcu_periph_clock_enable(RCU_AF);
                /*Configure PA1 PA2 PA3(TIMER1 CH1 CH2 CH3) as alternate function*/
                gpio_init(device->gpio_periph, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, device->gpio_pin);
            }else
            {
                return -RT_ERROR;
            }
            break;
    }
    if(rcu_per==0)
    {
        return RT_ERROR;
    }

    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;

    rcu_periph_clock_enable(rcu_per);

    timer_deinit(device->tim_handle);

    /* TIMER1 configuration */
    timer_initpara.prescaler         = 107;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 15999;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(device->tim_handle,&timer_initpara);

    /* CH1,CH2 and CH3 configuration in PWM mode1 */
    timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

    timer_channel_output_config(device->tim_handle,device->channel,&timer_ocintpara);

    /* CH1 configuration in PWM mode1,duty cycle 25% */
    timer_channel_output_pulse_value_config(device->tim_handle,device->channel,3999);
    timer_channel_output_mode_config(device->tim_handle,device->channel,TIMER_OC_MODE_PWM0);
    timer_channel_output_shadow_config(device->tim_handle,device->channel,TIMER_OC_SHADOW_DISABLE);

    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(device->tim_handle);
    //gd32_pwm_enable(device->tim_handle,RT_TRUE);

    return RT_EOK;
}
static int gd32_pwm_init(void)
{
    for (int var = 0; var < sizeof(gd32_pwm_list)/sizeof(struct gd32_pwm); var++)
    {
        if(gd32_hw_pwm_init(&gd32_pwm_list[var])!=RT_EOK)
        {
            continue;
        }
        rt_device_pwm_register(&gd32_pwm_list[var].pwm_device,gd32_pwm_list[var].name,&drv_ops,&gd32_pwm_list[var].tim_handle);
    }
    return RT_EOK;
}

#ifdef  RT_USING_PWM
INIT_DEVICE_EXPORT(gd32_pwm_init);
#endif

#if 0
void gpio_config(void);
void timer_config(void);
void set_cycle(unsigned char cycle);
/**
    \brief      configure the GPIO ports
    \param[in]  none
    \param[out] none
    \retval     none
  */
void gpio_config(void)
{
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_AF);

    /*Configure PA1 PA2 PA3(TIMER1 CH1 CH2 CH3) as alternate function*/
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
}

/**
    \brief      configure the TIMER peripheral
    \param[in]  none
    \param[out] none
    \retval     none
  */
void timer_config(void)
{
    /* -----------------------------------------------------------------------
    TIMER1 configuration: generate 3 PWM signals with 3 different duty cycles:
    TIMER1CLK = SystemCoreClock / 108 = 1MHz

    TIMER1 channel1 duty cycle = (4000/ 16000)* 100  = 25%
    ----------------------------------------------------------------------- */
    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;

    rcu_periph_clock_enable(RCU_TIMER1);

    timer_deinit(TIMER1);

    /* TIMER1 configuration */
    timer_initpara.prescaler         = 107;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 15999;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER1,&timer_initpara);

    /* CH1,CH2 and CH3 configuration in PWM mode1 */
    timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

    timer_channel_output_config(TIMER1,TIMER_CH_1,&timer_ocintpara);

    /* CH1 configuration in PWM mode1,duty cycle 25% */
    timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_1,3999);
    timer_channel_output_mode_config(TIMER1,TIMER_CH_1,TIMER_OC_MODE_PWM0);
    timer_channel_output_shadow_config(TIMER1,TIMER_CH_1,TIMER_OC_SHADOW_DISABLE);

    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER1);
    /* auto-reload preload enable */
    timer_enable(TIMER1);
}
void set_cycle(unsigned char cycle)
{
    unsigned short int mcycle=cycle*159;
    timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_1,mcycle);
}
#endif
